﻿//-----------------------------------------------------------------------
// <Copyright>
// * Copyright (C) 2022 RuYiAdmin All Rights Reserved
// </Copyright>
//-----------------------------------------------------------------------

using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Quartz;
using RuYiAdmin.Net.CommonInfrastructure.Configurations;
using RuYiAdmin.Net.CommonInfrastructure.Constants.Business;
using RuYiAdmin.Net.CommonInfrastructure.Constants.System;
using RuYiAdmin.Net.CommonInfrastructure.Enums.Business;
using RuYiAdmin.Net.CommonInfrastructure.Exceptions.Framework;
using RuYiAdmin.Net.CommonInfrastructure.Extensions.Business;
using RuYiAdmin.Net.CommonInfrastructure.Models;
using RuYiAdmin.Net.EntityDataModel.DataTransformationModel.SystemModel;
using RuYiAdmin.Net.EntityDataModel.EntityModel.SystemModel;
using RuYiAdmin.Net.EntityDataModel.SessionScope;
using RuYiAdmin.Net.ServiceLayer.BusinessService.Interface.FrameworkInterface;
using RuYiAdmin.Net.ServiceLayer.BusinessService.Interface.SystemInterface;
using RuYiAdmin.Net.WebApi.RuYiAdminCode.RuYiAdminAnnotation;
using RuYiAdmin.Net.WebApi.RuYiAdminCode.RuYiAdminBase;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace RuYiAdmin.Net.WebApi.RuYiAdminController.SystemController
{
    /// <summary>
    /// 计划任务控制器
    /// </summary>
    public class SysScheduleJobManagementController : RuYiAdminBaseController<SysScheduleJob>
    {
        #region 属性及构造函数

        /// <summary>
        /// 计划任务接口实例
        /// </summary>
        private readonly ISysScheduleJobService ScheduleJobService;

        /// <summary>
        /// Redis服务接口实例
        /// </summary>
        private readonly IRedisService RedisService;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ScheduleJobService"></param>
        /// <param name="RedisService"></param>
        public SysScheduleJobManagementController(ISysScheduleJobService ScheduleJobService,
                                                  IRedisService RedisService) : base(ScheduleJobService)
        {
            this.ScheduleJobService = ScheduleJobService;
            this.RedisService = RedisService;
        }

        #endregion

        #region 查询任务列表

        /// <summary>
        /// 查询任务列表
        /// </summary>
        /// <param name="searchCondition">查询条件</param>
        /// <returns>ActionResponseResult</returns>
        [HttpPost]
        [Log(OperationType.QueryList)]
        [Permission("job:query:list")]
        public async Task<IActionResult> Post(SearchCondition searchCondition)
        {
            var scheduleJobs = await this.RedisService.GetAsync<List<SysScheduleJob>>(RuYiGlobalConfig.SystemCacheConfig.ScheduleJobCacheName);
            scheduleJobs = scheduleJobs.AsQueryable().Where(searchCondition.ConvertToLamdaExpression<SysScheduleJob>()).ToList();

            if (!string.IsNullOrEmpty(searchCondition.Sort))
            {
                scheduleJobs = scheduleJobs.Sort(searchCondition.Sort);
            }

            int count = scheduleJobs.Count;
            scheduleJobs = scheduleJobs.Skip(searchCondition.PageIndex * searchCondition.PageSize).Take(searchCondition.PageSize).ToList();

            var actionResponseResult = QueryResponseResult<SysScheduleJob>.OK(count, scheduleJobs);
            return Ok(actionResponseResult);
        }

        #endregion

        #region 查询任务信息

        /// <summary>
        /// 查询任务信息
        /// </summary>
        /// <param name="jobId">任务编号</param>
        /// <returns>ActionResponseResult</returns>
        [HttpGet("{jobId}")]
        [Log(OperationType.QueryEntity)]
        [Permission("job:query:list")]
        public async Task<IActionResult> GetById(Guid jobId)
        {
            var scheduleJobs = await this.RedisService.GetAsync<List<SysScheduleJob>>(RuYiGlobalConfig.SystemCacheConfig.ScheduleJobCacheName);
            var actionResponseResult = ActionResponseResult.OK(scheduleJobs.Where(t => t.Id == jobId).FirstOrDefault());
            return Ok(actionResponseResult);
        }

        #endregion

        #region 新增计划任务

        /// <summary>
        /// 新增计划任务
        /// </summary>
        /// <param name="scheduleJob">计划任务对象</param>
        /// <returns>ActionResponseResult</returns>
        [HttpPost]
        [Log(OperationType.AddEntity)]
        [Permission("job:add:entity")]
        public async Task<IActionResult> Add([FromBody] SysScheduleJob scheduleJob)
        {
            //校验CronExpression表达式
            bool expression = CronExpression.IsValidExpression(scheduleJob.CronExpression);
            if (!expression)
            {
                throw new RuYiAdminCustomException(ExceptionMessage.InvalidCronExpressionExceptionMessage);
            }

            scheduleJob.JobStatus = JobStatus.Planning;

            //支持集群作业
            if (RuYiGlobalConfig.QuartzConfig.SupportGroup)
            {
                //保存为本节点定时任务
                scheduleJob.GroupId = RuYiGlobalConfig.QuartzConfig.GroupId;
            }

            var actionResponseResult = await this.ScheduleJobService.AddAsync(scheduleJob);

            #region 数据一致性维护

            var scheduleJobs = await this.RedisService.GetAsync<List<SysScheduleJob>>(RuYiGlobalConfig.SystemCacheConfig.ScheduleJobCacheName);

            //添加新数据
            scheduleJobs.Add(scheduleJob);

            //回写缓存
            await this.RedisService.SetAsync(RuYiGlobalConfig.SystemCacheConfig.ScheduleJobCacheName, scheduleJobs, -1);

            #endregion

            return Ok(actionResponseResult);
        }

        #endregion

        #region 编辑计划任务

        /// <summary>
        /// 编辑计划任务
        /// </summary>
        /// <param name="scheduleJob">计划任务对象</param>
        /// <returns>ActionResponseResult</returns>
        [HttpPut]
        [Log(OperationType.EditEntity)]
        [Permission("job:edit:entity")]
        public async Task<IActionResult> Put([FromBody] SysScheduleJob scheduleJob)
        {
            //校验CronExpression表达式
            bool expression = CronExpression.IsValidExpression(scheduleJob.CronExpression);
            if (!expression)
            {
                throw new RuYiAdminCustomException(ExceptionMessage.InvalidCronExpressionExceptionMessage);
            }

            var scheduleJobs = await this.RedisService.GetAsync<List<SysScheduleJob>>(RuYiGlobalConfig.SystemCacheConfig.ScheduleJobCacheName);
            var job = scheduleJobs.Where(t => t.Id == scheduleJob.Id).FirstOrDefault();

            if (job.JobStatus == JobStatus.Running)
            {
                throw new RuYiAdminCustomException(ExceptionMessage.StopJobExceptionMessage);
            }

            var actionResponseResult = await this.ScheduleJobService.UpdateAsync(scheduleJob);

            #region 数据一致性维护

            //删除旧数据
            scheduleJobs.Remove(job);

            //添加新数据
            scheduleJobs.Add(scheduleJob);

            //回写缓存
            await this.RedisService.SetAsync(RuYiGlobalConfig.SystemCacheConfig.ScheduleJobCacheName, scheduleJobs, -1);

            #endregion

            return Ok(actionResponseResult);
        }

        #endregion

        #region 删除计划任务

        /// <summary>
        /// 删除计划任务
        /// </summary>
        /// <param name="jobId">任务编号</param>
        /// <returns>ActionResponseResult</returns>
        [HttpDelete("{jobId}")]
        [Permission("job:del:entity")]
        [Log(OperationType.DeleteEntity)]
        public async Task<IActionResult> DeleteAsync(Guid jobId)
        {
            var scheduleJobs = await this.RedisService.GetAsync<List<SysScheduleJob>>(RuYiGlobalConfig.SystemCacheConfig.ScheduleJobCacheName);
            var job = scheduleJobs.Where(t => t.Id == jobId).FirstOrDefault();
            var user = RuYiSessionContext.GetCurrentUserInfo(this.HttpContext);

            if (!RuYiGlobalConfig.QuartzConfig.SupportGroup)
            {
                //单机版，不支持集群
                await this.ScheduleJobService.DeleteScheduleJobAsync(jobId, user.Id);

                #region 数据一致性维护

                //删除旧数据
                scheduleJobs.Remove(job);

                //回写缓存
                await this.RedisService.SetAsync(RuYiGlobalConfig.SystemCacheConfig.ScheduleJobCacheName, scheduleJobs, -1);

                #endregion

                return Ok(ActionResponseResult.OK());
            }
            else if (RuYiGlobalConfig.QuartzConfig.SupportGroup && job.GroupId != null && job.GroupId == RuYiGlobalConfig.QuartzConfig.GroupId)
            {
                //支持集群、且为本节点任务
                await this.ScheduleJobService.DeleteScheduleJobAsync(jobId, user.Id);

                #region 数据一致性维护

                //删除旧数据
                scheduleJobs.Remove(job);

                //回写缓存
                await this.RedisService.SetAsync(RuYiGlobalConfig.SystemCacheConfig.ScheduleJobCacheName, scheduleJobs, -1);

                #endregion

                return Ok(ActionResponseResult.OK());
            }
            else
            {
                var msg = JsonConvert.SerializeObject(new QuartzJobDTO()
                {
                    JobId = job.Id,
                    GroupId = job.GroupId,
                    Action = QuartzJobAction.Delete,
                    UserId = user.Id
                });

                //支持集群、且不为本节点任务
                this.RedisService.PublishMessage(RuYiGlobalConfig.QuartzConfig.ChanelName, msg);

                return Ok("OK");
            }
        }

        #endregion

        #region 启动计划任务

        /// <summary>
        /// 启动计划任务
        /// </summary>
        /// <param name="jobId">任务编号</param>
        /// <returns>ActionResponseResult</returns>
        [HttpGet("{jobId}")]
        [Log(OperationType.StartScheduleJob)]
        [Permission("schedule:job:add")]
        public async Task<IActionResult> StartScheduleJobAsync(Guid jobId)
        {
            var scheduleJobs = await this.RedisService.GetAsync<List<SysScheduleJob>>(RuYiGlobalConfig.SystemCacheConfig.ScheduleJobCacheName);
            var job = scheduleJobs.Where(t => t.Id == jobId).FirstOrDefault();
            var user = RuYiSessionContext.GetCurrentUserInfo(this.HttpContext);

            if (!RuYiGlobalConfig.QuartzConfig.SupportGroup)
            {
                //单机版，不支持集群
                await this.ScheduleJobService.StartScheduleJobAsync(jobId, user.Id);
            }
            else if (RuYiGlobalConfig.QuartzConfig.SupportGroup && job.GroupId != null && job.GroupId == RuYiGlobalConfig.QuartzConfig.GroupId)
            {
                //支持集群、且为本节点任务
                await this.ScheduleJobService.StartScheduleJobAsync(jobId, user.Id);
            }
            else
            {
                var msg = JsonConvert.SerializeObject(new QuartzJobDTO()
                {
                    JobId = job.Id,
                    GroupId = job.GroupId,
                    Action = QuartzJobAction.Start,
                    UserId = user.Id
                });

                //支持集群、且不为本节点任务
                this.RedisService.PublishMessage(RuYiGlobalConfig.QuartzConfig.ChanelName, msg);
            }

            return Ok("OK");
        }

        #endregion

        #region 暂停计划任务

        /// <summary>
        /// 暂停计划任务
        /// </summary>
        /// <param name="jobId">任务编号</param>
        /// <returns>ActionResponseResult</returns>
        [HttpGet("{jobId}")]
        [Log(OperationType.PauseScheduleJob)]
        [Permission("schedule:job:pause")]
        public async Task<IActionResult> PauseScheduleJob(Guid jobId)
        {
            var scheduleJobs = await this.RedisService.GetAsync<List<SysScheduleJob>>(RuYiGlobalConfig.SystemCacheConfig.ScheduleJobCacheName);
            var job = scheduleJobs.Where(t => t.Id == jobId).FirstOrDefault();
            var user = RuYiSessionContext.GetCurrentUserInfo(this.HttpContext);

            if (!RuYiGlobalConfig.QuartzConfig.SupportGroup)
            {
                //单机版，不支持集群
                await this.ScheduleJobService.PauseScheduleJobAsync(jobId, user.Id);
            }
            else if (RuYiGlobalConfig.QuartzConfig.SupportGroup && job.GroupId != null && job.GroupId == RuYiGlobalConfig.QuartzConfig.GroupId)
            {
                //支持集群、且为本节点任务
                await this.ScheduleJobService.PauseScheduleJobAsync(jobId, user.Id);
            }
            else
            {
                var msg = JsonConvert.SerializeObject(new QuartzJobDTO()
                {
                    JobId = job.Id,
                    GroupId = job.GroupId,
                    Action = QuartzJobAction.Pause,
                    UserId = user.Id
                });

                //支持集群、且不为本节点任务
                this.RedisService.PublishMessage(RuYiGlobalConfig.QuartzConfig.ChanelName, msg);
            }

            return Ok("OK");
        }

        #endregion

        #region 恢复计划任务

        /// <summary>
        /// 恢复计划任务
        /// </summary>
        /// <param name="jobId">任务编号</param>
        /// <returns>ActionResponseResult</returns>
        [HttpGet("{jobId}")]
        [Log(OperationType.ResumeScheduleJob)]
        [Permission("schedule:job:resume")]
        public async Task<IActionResult> ResumeScheduleJob(Guid jobId)
        {
            var scheduleJobs = await this.RedisService.GetAsync<List<SysScheduleJob>>(RuYiGlobalConfig.SystemCacheConfig.ScheduleJobCacheName);
            var job = scheduleJobs.Where(t => t.Id == jobId).FirstOrDefault();
            var user = RuYiSessionContext.GetCurrentUserInfo(this.HttpContext);

            if (!RuYiGlobalConfig.QuartzConfig.SupportGroup)
            {
                //单机版，不支持集群
                await this.ScheduleJobService.ResumeScheduleJobAsync(jobId, user.Id);
            }
            else if (RuYiGlobalConfig.QuartzConfig.SupportGroup && job.GroupId != null && job.GroupId == RuYiGlobalConfig.QuartzConfig.GroupId)
            {
                //支持集群、且为本节点任务
                await this.ScheduleJobService.ResumeScheduleJobAsync(jobId, user.Id);
            }
            else
            {
                var msg = JsonConvert.SerializeObject(new QuartzJobDTO()
                {
                    JobId = job.Id,
                    GroupId = job.GroupId,
                    Action = QuartzJobAction.Resume,
                    UserId = user.Id
                });

                //支持集群、且不为本节点任务
                this.RedisService.PublishMessage(RuYiGlobalConfig.QuartzConfig.ChanelName, msg);
            }

            return Ok("OK");
        }

        #endregion
    }
}
